#ifndef SESSION_H
#define SESSION_H

//Test data structures defenition

#include <ctime> 

#include <limits>

#include <boost/scoped_array.hpp>
#include <boost/cstdint.hpp>
#include <boost/random.hpp>
#include <boost/generator_iterator.hpp>

#include "pgwb.h"

#define SPEED_TEST
#define USE_STD_STRING_BUFFER

#ifdef USE_STD_STRING_BUFFER
std::string buffer;
void bufferReserve(size_t size) {buffer.reserve(size);}
#endif

namespace Porridge
{

#ifdef SPEED_TEST
typedef boost::minstd_rand base_generator_type;
typedef boost::uniform_int<> distribution_type;
typedef boost::variate_generator<base_generator_type&, distribution_type> gen_type;
#endif

template<typename T>
class Buffer
{
public:
    std::string data;
    size_t sizeLimit;
    T * output;

    void write(const char * c, size_t size);
    void flush();
};

template<typename T>
Buffer<T>& operator<<(Buffer<T>& out, const std::pair<const char*, size_t> & data)
{
    out.write(data.first, data.second);
}

template<typename T>
void Buffer<T>::write(const char * c, size_t size)
{
    data.append(c, size);
    if(data.size() > sizeLimit)
        flush();
}

template<typename T>
void Buffer<T>::flush()
{
    output->write(data.data(), data.size());
    data.clear();
}

struct Session
{
    //CREATE TABLE session
    //(
    //    s_time timestamp NOT NULL,
    //    ip_src inet NOT NULL,
    //    ip_dst inet NOT NULL,
    //    port_src smallint NOT NULL,
    //    port_dst smallint NOT NULL,
    //    proto_id integer NOT NULL,
    //    channel_id integer NOT NULL,
    //    session_id integer NOT NULL
    //);
    
#ifdef SPEED_TEST
    //debug only: init session with random date
    Session(int32_t rowID)
    {        
        seconds = std::time(0);
        microseconds = 0;
        base_generator_type generator(static_cast<unsigned int>(seconds) +
                                      reinterpret_cast<int64_t>(this));
        gen_type ip_generator(generator, distribution_type(1, std::numeric_limits<int32_t>::max() - 2));
        gen_type port_generator(generator, distribution_type(1, std::numeric_limits<int16_t>::max() - 2));
        gen_type proto_generator(generator, distribution_type(1, 50));
        gen_type channel_generator(generator, distribution_type(1, 10));

        ip_src = ip_generator();
        ip_dst = ip_generator();
        port_src = port_generator();
        port_dst = port_generator();
        proto_id = proto_generator();
        channel_id = channel_generator();
        session_id = rowID;
        session_table_id = 0;
    }

#endif
    
    template<typename T>
    bool write(int64_t table_id, T * output)
    {
        PGWB::writeRowHeader(output, fieldsCount());
        PGWB::writeTimestampField(output, seconds, microseconds);
        PGWB::writeIPField(output, ip_src);
        PGWB::writeIPField(output, ip_dst);
        PGWB::writeIntegerField(output, int16_t(port_src));
        PGWB::writeIntegerField(output, port_dst);
        PGWB::writeIntegerField(output, proto_id);
        PGWB::writeIntegerField(output, channel_id);
        PGWB::writeIntegerField(output, session_id);
        PGWB::writeIntegerField(output, table_id);
        return true;
    }
    int fieldsCount() const {return 9;}
    
    long seconds;
    long microseconds;
    int32_t ip_src;
    int32_t ip_dst;
    int16_t port_src;
    int16_t port_dst;
    int32_t proto_id;
    int32_t channel_id;
    int32_t session_id;
    int64_t session_table_id;
};

struct UIN
{
//    CREATE TABLE uin
//    (
//        uin varchar NOT NULL,
//        uin_type_id integer NOT NULL,
//        from_to bool NOT NULL,
//        session_id integer NOT NULL
//    );
    
#ifdef SPEED_TEST
    static void get_random_string(base_generator_type generator, char * s, size_t length)
    {
        static const char alphanum[] = "0123456789"
                                       "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                                       "abcdefghijklmnopqrstuvwxyz"
                                       "@#$";
        gen_type alphanum_generator(generator, distribution_type(0, (arraysize(alphanum) - 2)));
        for (int i = 0; i < length; ++i)
            s[i] = alphanum[alphanum_generator()];
        s[length] = 0;
    }

    UIN(size_t uinLength, uint32_t rowID) : uin(new char[uinLength + 1])
    {
        base_generator_type generator(static_cast<unsigned int>(std::time(0)) +
                                      reinterpret_cast<int64_t>(this));
        gen_type uin_type_generator(generator, distribution_type(0, 50));
        gen_type from_to_generator(generator, distribution_type(0, 50));
        gen_type session_id_generator(generator, distribution_type(0, rowID));

        get_random_string(generator, uin.get(), uinLength);
        uin_type_id = uin_type_generator();
        from_to = from_to_generator();
        session_id = session_id_generator();
        session_table_id = 0;
    }

#endif
    
    template<typename T>
    bool write(int64_t table_id, T * output)
    {
        PGWB::writeRowHeader(output, fieldsCount());
        PGWB::writeStringField(output, uin.get());
        PGWB::writeIntegerField(output, uin_type_id);
        PGWB::writeIntegerField(output, from_to);
        PGWB::writeIntegerField(output, session_id);
        PGWB::writeIntegerField(output, table_id);
        return true;
    }
    int fieldsCount() const {return 5;}
    
    boost::scoped_array<char> uin;
    int32_t uin_type_id;
    int16_t from_to;
    int32_t session_id;
    //stub
    int64_t session_table_id;
};

size_t uinSize() {return sizeof(UIN) - sizeof(boost::scoped_array<char>);}
size_t sessionSize() {return sizeof(Session);}

}//namespace Porridge

#endif // SESSION_H
